package ogmios.dialogs;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.filechooser.*;
import java.io.*;
import java.util.List;
import java.util.jar.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.util.regex.Pattern;
import ogmios.core.*;
import ogmios.components.*;
import ogmios.components.archivers.*;
import static ogmios.components.FileTree.FileNode;
import static ogmios.components.archivers.Archiver.ArchiveProperties;
/**
 * A GUI for constructing different types of Archives.
 */
public class ArchiveBuilder extends JDialog
{
  private final Map<String, ArchiverTab> archiverTabs = new HashMap<String, ArchiverTab> ();

  private final FileTree tree;
  private JPanel optionsPanel;
  private JButton compress;
  private JTextField archiveName;
  private JButton selectArchiveDirectory;
  private JTextField archiveDirectory;

  private final JButton addFolder;
  private final JButton addFile;
  private final JButton addFilesByFilter;
  private final JButton removeSelected;
  private final JComboBox selectArchiver;
  public ArchiveBuilder()
  {
    this(null);
  }
  public ArchiveBuilder(Frame owner)
  {
    super(owner);
    setModal(true);
    setTitle("Archive Builder");
    tree = new FileTree("Contents");
    selectArchiver = new JComboBox();
    archiveName = new JTextField("", 17);
    archiveName.setMaximumSize(archiveName.getPreferredSize());
    archiveDirectory = new JTextField("", 17);
    archiveDirectory.setMaximumSize(archiveDirectory.getPreferredSize());
    selectArchiveDirectory = new JButton("Select Archive Directory");
    addFile = new JButton("Add File");
    addFolder = new JButton("Add Folder");
    addFilesByFilter = new JButton("Add Files By Filter");
    removeSelected = new JButton("Remove Selected");
   
    compress = new JButton("Compress");
    compress.addActionListener(
      new ActionListener()
      {
       public void actionPerformed(ActionEvent e)
       {
        if (addFile.isEnabled())
        {
         //compress.setEnabled(false);
         compress.setText("Stop");
         addFile.setEnabled(false);
         addFolder.setEnabled(false);
         addFilesByFilter.setEnabled(false);
         removeSelected.setEnabled(false);
         archiveDirectory.setEnabled(false);
         selectArchiver.setEnabled(false);
         selectArchiveDirectory.setEnabled(false);
         archiveName.setEnabled(false);   
          new Thread()
          {
            {
             setDaemon(true);
            }
            public void run()
            {
             try
             {
              String name;
              String path;
              ArchiverTab archiverTab = archiverTabs.get(name=(String)selectArchiver.getSelectedObjects()[0]);
              archiverTab.archiver.archive(
                new File(path=archiveDirectory.getText() + File.separator + archiveName.getText()), tree.getModel(), archiverTab.properties);
               JOptionPane.showMessageDialog(null, "Build was successful!\n\n" + name+ " saved at " + path, "Archive:Build Complete!", JOptionPane.INFORMATION_MESSAGE);
             }
             catch(Throwable ex)
             {
               OgmiosUtilities.showErrorBox(ArchiveBuilder.this, ex, "Archiver: Archiving Error");
             }
             compress.setEnabled(true);
             compress.setText("Compress");
             addFile.setEnabled(true);
             addFolder.setEnabled(true);
             addFilesByFilter.setEnabled(true);
             removeSelected.setEnabled(true);
             archiveDirectory.setEnabled(true);
             selectArchiver.setEnabled(true);
             selectArchiveDirectory.setEnabled(true);
             archiveName.setEnabled(true);   
            }
           }.start();
          }
          else
          {
            archiverTabs.get(selectArchiver.getSelectedObjects()[0]).archiver.stop();       
            compress.setEnabled(false);
          }        
        }
       }
       );
     
    selectArchiver.addItemListener(
     new ItemListener()
     {
       public void itemStateChanged(ItemEvent e)
       {
         if (e.getStateChange()==ItemEvent.SELECTED)
         {
          optionsPanel.add(archiverTabs.get(selectArchiver.getSelectedObjects()[0]));          
          ArchiveBuilder.this.validate();
          ArchiveBuilder.this.repaint();
         }
         else
         {
           optionsPanel.remove(optionsPanel.getComponentCount()-1);
         }
        }
      }
     );

    selectArchiveDirectory.addActionListener(
      new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        JFileChooser folderDialog = OgmiosFileChooser.getFolderDialog();
        folderDialog.setMultiSelectionEnabled(false);
        if (folderDialog.showOpenDialog(ArchiveBuilder.this) == JFileChooser.APPROVE_OPTION)
        {
          archiveDirectory.setText(folderDialog.getSelectedFile().getPath());
          archiveDirectory.select(0, 0);
        }
      }
    }
    );

    addFile.addActionListener(
      new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        tree.addFile();
      }
    }
    );

    addFolder.addActionListener(
      new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        tree.addFolder();
      }
    }
    );


    addFilesByFilter.addActionListener(
      new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        tree.addFilesByFilter();
      }
    }
    );

    removeSelected.addActionListener(
      new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        tree.removeSelected();
      }
    }
    );

   


    Container contents = getContentPane();
    contents.add(selectArchiver, BorderLayout.NORTH);
    JPanel bigPanel = new JPanel(new BorderLayout());
    optionsPanel = new JPanel();
    optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS));

    optionsPanel.add(new JLabel("Archive Info", JLabel.CENTER));
    JPanel dualPanel = new JPanel();
    dualPanel.setLayout(new BoxLayout(dualPanel, BoxLayout.X_AXIS));
    dualPanel.add(new JLabel("Archive Name:"));
    dualPanel.add(OgmiosUtilities.undoable(archiveName));
    dualPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    optionsPanel.add(dualPanel);

    dualPanel = new JPanel();
    dualPanel.setLayout(new BoxLayout(dualPanel, BoxLayout.X_AXIS));
    dualPanel.add(selectArchiveDirectory);
    dualPanel.add(OgmiosUtilities.undoable(archiveDirectory));
    dualPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    optionsPanel.add(dualPanel);
    
    optionsPanel.add(new JLabel("Manage Files"));
    optionsPanel.add(addFile);
    optionsPanel.add(addFolder);
    optionsPanel.add(addFilesByFilter);
    optionsPanel.add(removeSelected);
    addFile.setAlignmentX(Component.LEFT_ALIGNMENT);
    addFolder.setAlignmentX(Component.LEFT_ALIGNMENT);
    addFilesByFilter.setAlignmentX(Component.LEFT_ALIGNMENT);
    removeSelected.setAlignmentX(Component.LEFT_ALIGNMENT);

    
    JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(tree), bigPanel);
    bigPanel.add(optionsPanel);
    bigPanel.add(compress, BorderLayout.SOUTH); 
    contents.add(splitter);
    pack();


    addWindowListener(
    new WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        if (compress.isEnabled() && compress.getText().equals("Stop") &&
         JOptionPane.showConfirmDialog(ArchiveBuilder.this, "Do you want to stop the archiving?", "Archiving", JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION)==
         JOptionPane.YES_OPTION) 
        archiverTabs.get(selectArchiver.getSelectedObjects()[0]).archiver.stop();
      }
    }
   );
  }

 /**
  * Adds given ArchiverTab to this ArchiveBuilder if no ArchiverTab with the same name (as in, archiverTab.getName()) is already registered
  */
  public void registerArchiverTab(ArchiverTab archiverTab)
 {
  if (archiverTabs.get(archiverTab.getName())==null)
  {
   addArchiverTab(archiverTab);
  }
 }
 /**
 * Adds given Archiver to this ArchiveBuilder if no ArchiverTab with the same name (as in, archiver.getName()) is already registered
 */
 public void registerArchiver(Archiver archiver)
 {
  if (archiverTabs.get(archiver.getName())==null)
  {
    addArchiverTab(new ArchiverTab(archiver));
  }
 }
 /**
 * Adds given archiver tab
 */
 private void addArchiverTab(ArchiverTab archiverTab)
 {
  String name;
  archiverTabs.put(name=archiverTab.archiver.getName(), archiverTab);
  selectArchiver.addItem(name);
 }
 
 /**
 * Show ArchiveBuilder
 */
 public void showDialog(Component host, String archiverName)
 {
  if (archiverTabs.get(archiverName)!=null)
   selectArchiver.setSelectedItem(archiverName);
  setLocationRelativeTo(host);
  setVisible(true);
 }

  public static void main(String[] args)
  {
   ArchiveBuilder archiveBuilder = new ArchiveBuilder();
   archiveBuilder.registerArchiver( new ZipArchiver());
   archiveBuilder.registerArchiver( new JarArchiver());
   archiveBuilder.setVisible(true);
  }

  /**
  * A panel containing the basic methods and GUI components needed to use <br>
  * the underlying archiver. The name, location, file tree, and compress button
  * are provided by the ArchiveBuilder. 
  */
  public static class ArchiverTab extends JPanel
  {
   public final Archiver archiver;
   public final Map<String, Object> properties;
   public static final ActionListener BOOLEAN_PROPERTY_CHANGE = 
    new ActionListener()
    {
     public void actionPerformed(ActionEvent e)
     {
       JCheckBox propertyChanger = (JCheckBox)e.getSource();
       ((ArchiverTab)propertyChanger.getParent()).properties.put(propertyChanger.getName(), propertyChanger.isSelected());
     }
    };
   public static final ActionListener FILE_PROPERTY_CHANGE = 
    new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        JFileChooser folderDialog = OgmiosFileChooser.getFolderDialog();
        folderDialog.setMultiSelectionEnabled(false);

        if (folderDialog.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
        {
          JButton propertyChanger = (JButton)e.getSource();
          JPanel parentPanel = (JPanel)propertyChanger.getParent();
          JTextField textField = (JTextField)parentPanel.getComponent(1);
          textField.setText(folderDialog.getSelectedFile().getPath());
          textField.select(0, 0);
        }
      }
    };
   public DocumentListener getDefaultTextListener(final String property)
   {
    return 
     new DocumentListener()
     {
       public void changedUpdate(DocumentEvent e)
       {
       }
       public void insertUpdate(DocumentEvent e)
       {
         updateProperty(e);
       }
       public void removeUpdate(DocumentEvent e)
       {
         updateProperty(e);
       }
       public void updateProperty(DocumentEvent e)
       {
         Document document = e.getDocument();
         try
         {
          properties.put(property, document.getText(0, document.getLength()));
         }
         catch(BadLocationException locationException)
         {
           OgmiosUtilities.showErrorBox(null, locationException, "Property:Parsing Error");
         }
       }
     };
   }
   public DocumentListener getFileTextListener(final String property)
   {
    return 
     new DocumentListener()
     {
       public void changedUpdate(DocumentEvent e)
       {
       }
       public void insertUpdate(DocumentEvent e)
       {
         updateProperty(e);
       }
       public void removeUpdate(DocumentEvent e)
       {
         updateProperty(e);
       }
       public void updateProperty(DocumentEvent e)
       {
         Document document = e.getDocument();
         try
         {
          properties.put(property, new File(document.getText(0, document.getLength())));
         }
         catch(BadLocationException locationException)
         {
           OgmiosUtilities.showErrorBox(null, locationException, "Property:Parsing Error");
         }
       }
     };
   }
   public ArchiverTab(Archiver archiver)
   {
     this.archiver = archiver;
     this.properties = new HashMap<String, Object>();
     setAlignmentX(Component.LEFT_ALIGNMENT);
     setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
     Map<String, ArchiveProperties> archiveProperties = archiver.properties();
     for (String property : archiveProperties.keySet())
     {
       switch(archiveProperties.get(property))
       {
         case SINGLE_STRING:
         {
          JPanel dualPanel = new JPanel();
          dualPanel.setLayout(new BoxLayout(dualPanel, BoxLayout.X_AXIS));
          dualPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
          dualPanel.add(new JLabel(property + ":"));
          JTextField text = new JTextField("",17);
          text.setMaximumSize(text.getPreferredSize());
          text.getDocument().addDocumentListener(getDefaultTextListener(property));
          dualPanel.add(OgmiosUtilities.undoable(text));
          add(dualPanel);   
          this.properties.put(property,"");
          break;
         }
         case FILE:
         {
          JPanel dualPanel = new JPanel(); 
          dualPanel.setLayout(new BoxLayout(dualPanel, BoxLayout.X_AXIS));
          dualPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
          JButton changeProperty = new JButton(property);
          changeProperty.setName(property);
          changeProperty.addActionListener(FILE_PROPERTY_CHANGE);
          dualPanel.add(changeProperty);
          JTextField text = new JTextField("",17);
          text.setMaximumSize(text.getPreferredSize());
          text.getDocument().addDocumentListener(getFileTextListener(property));
          dualPanel.add(OgmiosUtilities.undoable(text));
          add(dualPanel);
          this.properties.put(property,new File(""));
          break;
         }
         case BOOLEAN:
         {
          JCheckBox propertyChanger = new JCheckBox(property, false);
          propertyChanger.addActionListener(BOOLEAN_PROPERTY_CHANGE);
          propertyChanger.setAlignmentX(Component.LEFT_ALIGNMENT);
          add(propertyChanger);
          this.properties.put(property, false);
          break;
         }
         default:
         {
          JTextArea multiLineText = new JTextArea();
          multiLineText.getDocument().addDocumentListener(getDefaultTextListener(property));
          multiLineText.setBorder(BorderFactory.createTitledBorder(property));
          multiLineText.setAlignmentX(JComponent.LEFT_ALIGNMENT);
          add(new JScrollPane(OgmiosUtilities.undoable(multiLineText)));
          this.properties.put(property, "");
         }
       }
     }
    }
  }

}
